package logging

import (
	"fmt"
	"os"
	"runtime"
	"time"
)

const (
	LogFATAL = iota
	LogERROR
	LogWARN
	LogINFO
	LogDEBUG
)

var Log2Log = LogWARN
var Log2Con = LogINFO

const defaultLogDir = "log/"
const RTStr = runtime.GOOS + "-" + runtime.GOARCH

var fLog *os.File
var next int64


func newLog(logdir string) {
	t := time.Now()
	timestr := fmt.Sprintf(logdir+"log_%04d-%02d-%02d.txt", t.Year(), t.Month(), t.Day())
	y, m, d := t.Date()
	next = time.Date(y, m, d+1, 0, 0, 0, 1, time.Local).Unix()
	var err error
	fLog, err = os.OpenFile(timestr, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(logdir, os.ModePerm)
			if err != nil {
				panic("LogDir Creation Error: " + err.Error())
			}
			fLog, err = os.OpenFile(timestr, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
			if err != nil {
				goto Err
			}
		} else {
			goto Err
		}
		return
	Err:
		panic("Log Creation Error: " + err.Error())
	}
	return
}

// stub
var FATAL = func(message ...string){}
var ERROR = func(message ...string){}
var WARN = func(message ...string){}
var INFO = func(message ...string){}
var DEBUG = func(message ...string){}


func Init(appName, appver string) {
	newLog(defaultLogDir)
	switch Log2Con {
	case LogDEBUG:
		DEBUG = genToSTD("D")
		fallthrough
	case LogINFO:
		INFO = genToSTD("I")
		fallthrough
	case LogWARN:
		WARN = genToSTD("W")
		fallthrough
	case LogERROR:
		ERROR = genToSTD("E")
		fallthrough
	case LogFATAL:
		FATAL = genToSTD("F")
	}

	switch Log2Log {
	case LogDEBUG:
		DEBUG = genToFile("D")
		fallthrough
	case LogINFO:
		INFO = genToFile("I")
		fallthrough
	case LogWARN:
		WARN = genToFile("W")
		fallthrough
	case LogERROR:
		ERROR = genToFile("E")
		fallthrough
	case LogFATAL:
		FATAL = genToFile("F")
	}
	INFO(fmt.Sprintf("%s %s running at %s", appName, appver, RTStr))
}

const smallsString = "00010203040506070809" +
	"10111213141516171819" +
	"20212223242526272829" +
	"30313233343536373839" +
	"40414243444546474849" +
	"50515253545556575859" +
	"60616263646566676869" +
	"70717273747576777879" +
	"80818283848586878889" +
	"90919293949596979899"

func genTimeStr(logType string) []byte {
	t := time.Now()
	h, m, s := t.Clock()
	if t.Unix() > next { // Day wrapped
		fLog.Close()
		newLog(defaultLogDir)
	}
	n := int32(t.Nanosecond() / 100000) // 2264
	buf := make([]byte, 24)[:0]
	buf = append(buf, '[')
	buf = append(buf, smallsString[h*2:h*2+2]...)
	buf = append(buf, ':')
	buf = append(buf, smallsString[m*2:m*2+2]...)
	buf = append(buf, ':')
	buf = append(buf, smallsString[s*2:s*2+2]...)
	buf = append(buf, '.')
	k := n / 100 // 22
	n %= 100 // 64
	buf = append(buf, smallsString[k*2:k*2+2]...)
	buf = append(buf, smallsString[n*2:n*2+2]...)
	buf = append(buf, "] "...)
	buf = append(buf, logType...)
	return buf
}

func genToSTD(logType string) func(message ...string){
	return func(message ...string) {
		buf := genTimeStr(logType)
		for _, m := range message {
			buf = append(buf, ' ')
			buf = append(buf, m...)
		}
		buf = append(buf, '\n')
		_, _ = os.Stdout.Write(buf)
	}
}

func genToFile(logType string) func(message ...string){
	return func(message ...string) {
		buf := genTimeStr(logType)
		for _, m := range message {
			buf = append(buf, ' ')
			buf = append(buf, m...)
		}
		buf = append(buf, '\n')
		_, _ = os.Stdout.Write(buf)
		_, err := fLog.Write(buf)
		if err != nil {
			time.Sleep(time.Second)
			_, err := fLog.Write(buf)
			if err != nil {
				panic("Log Write LogERROR: " + err.Error())
			}
		}
	}
}